// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "vfsservice.h"

#include "projectstore/projectstore.h"

#include <zencore/logging.h>
#include <zenvfs/vfs.h>

#if ZEN_WITH_VFS

#	include <memory>
#	include <unordered_map>

namespace zen {

struct VfsOplogDataSource : public VfsTreeDataSource
{
	VfsOplogDataSource(std::string_view ProjectId, std::string_view OplogId, Ref<ProjectStore> InProjectStore);

	virtual void ReadNamedData(std::string_view Path, void* Buffer, uint64_t ByteOffset, uint64_t ByteCount) override;
	virtual void ReadChunkData(const Oid& ChunkId, void* Buffer, uint64_t ByteOffset, uint64_t ByteCount) override;
	virtual void PopulateDirectory(std::string NodePath, VfsTreeNode& DirNode) override;

private:
	std::string		  m_ProjectId;
	std::string		  m_OplogId;
	Ref<ProjectStore> m_ProjectStore;
};

struct VfsCacheDataSource : public VfsTreeDataSource
{
	VfsCacheDataSource(std::string_view NamespaceId, std::string_view BucketId, Ref<ZenCacheStore> InCacheStore);
	~VfsCacheDataSource();

	virtual void ReadNamedData(std::string_view Path, void* Buffer, uint64_t ByteOffset, uint64_t ByteCount) override;
	virtual void ReadChunkData(const Oid& ChunkId, void* Buffer, uint64_t ByteOffset, uint64_t ByteCount) override;
	virtual void PopulateDirectory(std::string NodePath, VfsTreeNode& DirNode) override;

private:
	std::string		   m_NamespaceId;
	std::string		   m_BucketId;
	Ref<ZenCacheStore> m_CacheStore;
};

struct VfsServiceDataSource : public VfsTreeDataSource
{
	VfsServiceDataSource(VfsService::Impl* VfsImpl) : m_VfsImpl(VfsImpl) {}

	virtual void ReadNamedData(std::string_view Path, void* Buffer, uint64_t ByteOffset, uint64_t ByteCount) override;
	virtual void ReadChunkData(const Oid& ChunkId, void* Buffer, uint64_t ByteOffset, uint64_t ByteCount) override;
	virtual void PopulateDirectory(std::string NodePath, VfsTreeNode& DirNode) override;

private:
	VfsService::Impl* m_VfsImpl = nullptr;

	RwLock													 m_Lock;
	std::unordered_map<std::string, Ref<VfsOplogDataSource>> m_OplogSourceMap;
	std::unordered_map<std::string, Ref<VfsCacheDataSource>> m_CacheSourceMap;

	Ref<VfsOplogDataSource> GetOplogDataSource(std::string_view ProjectId, std::string_view OplogId);
	Ref<VfsCacheDataSource> GetCacheDataSource(std::string_view NamespaceId, std::string_view BucketId);
};

//////////////////////////////////////////////////////////////////////////

struct VfsService::Impl
{
	Impl();
	~Impl();

	void Mount(std::string_view MountPoint);
	void Unmount();
	void AddService(Ref<ProjectStore>&&);
	void AddService(Ref<ZenCacheStore>&&);

	inline std::string GetMountpointPath() { return m_MountpointPath; }
	inline bool		   IsVfsRunning() const { return !!m_VfsHost.get(); }

private:
	Ref<ProjectStore>		  m_ProjectStore;
	Ref<ZenCacheStore>		  m_ZenCacheStore;
	Ref<VfsServiceDataSource> m_VfsDataSource;
	std::string				  m_MountpointPath;

	std::unique_ptr<VfsHost> m_VfsHost;
	std::thread				 m_VfsThread;
	Event					 m_VfsThreadRunning;
	std::exception_ptr		 m_VfsThreadException;

	void RefreshVfs();
	void VfsThread();

	friend struct VfsServiceDataSource;
};

}  // namespace zen

#endif
